Conversation
Fix #342 fix canvas component
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughChat, graph, UI, tests, and infra were refactored to centralize graph state via GraphRef, expose messaging/path types and state as props, replace the legacy ForceGraph2D with a new ForceGraph/canvas integration, add desktop/mobile refs and UI primitives (carousel/drawer/switch), expand E2E helpers/tests, add docker-compose, and bump dependencies. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant ChatUI as Chat component
participant API as /api/chat (or repo)
participant Canvas as ForceGraph via GraphRef
User->>ChatUI: submit question or request path
ChatUI->>API: fetch response / path data
API-->>ChatUI: returns messages and path elements
ChatUI->>Canvas: merge/update graph data via canvasRef (set data, mark path flags)
Canvas-->>ChatUI: engine stop / zoomToFit complete (events)
ChatUI-->>User: render messages and focused graph
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
PR Summary
Hi! Looks like you've reached your API usage limit. You can increase it from your account settings page here: app.greptile.com/settings/billing/code-review
💡 (1/5) You can manually trigger the bot by mentioning @greptileai in a comment!
9 file(s) reviewed, no comment(s)
Edit PR Review Bot Settings | Greptile
There was a problem hiding this comment.
Actionable comments posted: 2
🔭 Outside diff range comments (2)
app/components/graphView.tsx (1)
Line range hint
105-166: Improve double-click detection implementation.The current implementation has potential issues:
- The double-click detection uses a fixed 1000ms threshold which might not match the system's double-click speed setting.
- The implementation doesn't handle the case where a user clicks different nodes within the threshold.
Consider this improved implementation:
-const lastClick = useRef<{ date: Date, name: string }>({ date: new Date(), name: "" }) +const lastClick = useRef<{ time: number, id: number }>({ time: 0, id: 0 }) -const isDoubleClick = now.getTime() - date.getTime() < 1000 && name === node.name +const isDoubleClick = (now.getTime() - lastClick.current.time < 500) && (lastClick.current.id === node.id) +lastClick.current = { time: now.getTime(), id: node.id }app/components/chat.tsx (1)
Line range hint
157-215: Improve zoom functionality implementation.Several improvements can be made to the zoom functionality:
- The setTimeout with 0ms delay is unnecessary
- Magic numbers should be constants
- Add proper error handling for undefined chart
Apply these changes:
+const ZOOM_DURATION = 1000; +const ZOOM_PADDING = 150; const handleSetSelectedPath = (p: PathData) => { const chart = chartRef.current - if (!chart) return + if (!chart) { + console.warn('Chart reference is not available'); + return; + } // ... rest of the function ... - setTimeout(() => { - chart.zoomToFit(1000, 150, (n: NodeObject<Node>) => p.nodes.some(node => node.id === n.id)); - }, 0) + chart.zoomToFit(ZOOM_DURATION, ZOOM_PADDING, (n: NodeObject<Node>) => p.nodes.some(node => node.id === n.id)); }🧰 Tools
🪛 Biome (1.9.4)
[error] 163-163: Comparing to itself is potentially pointless.
(lint/suspicious/noSelfCompare)
🧹 Nitpick comments (5)
e2e/tests/chat.spec.ts (1)
113-113: Remove unnecessary empty line within the loop.The empty line within the loop affects readability without adding value.
for (let i = 0; i < 3; i++) { await chat.sendMessage(Node_Question); const result = await chat.getTextInLastChatElement(); const number = result.match(/\d+/g)?.[0]!; responses.push(number); - }app/components/model.ts (2)
179-215: Add error handling for edge cases.While the node creation logic is sound, it would benefit from additional error handling for edge cases.
let source = this.nodesMap.get(edgeData.src_node) let target = this.nodesMap.get(edgeData.dest_node) + if (edgeData.src_node === undefined || edgeData.dest_node === undefined) { + console.warn('Invalid edge data: Missing source or destination node ID'); + return; + } if (!source) {
229-253: Add documentation for curve calculation logic.The curve calculation logic is complex and would benefit from documentation explaining the mathematical reasoning.
Add a comment block explaining the curve calculation:
+ // Calculate curve values for graph edges: + // - For self-loops (start === end): + // - Even indices: negative values starting from -3 + // - Odd indices: positive values starting from 2 + // - For regular edges: + // - Even indices: negative values starting from 0 + // - Odd indices: positive values starting from 1 + // The final curve value is scaled by 0.1 to create subtle curves newElements.links.forEach(link => {e2e/logic/POM/codeGraph.ts (1)
284-285: Consider removing redundant delay.The code waits for the loading image to be hidden and then adds an additional 2-second delay, which might be unnecessary.
Consider removing the redundant delay:
async getTextInLastChatElement(): Promise<string>{ await this.page.waitForSelector('img[alt="Waiting for response"]', { state: 'hidden' }); - await delay(2000); return (await this.lastElementInChat.textContent())!; }app/components/chat.tsx (1)
Line range hint
272-313: Extract common zoom functionality.The zoom logic is duplicated between
handleSetSelectedPathandhandleSubmit. Consider extracting it into a reusable function.Apply these changes:
+const ZOOM_DURATION = 1000; +const ZOOM_PADDING = 150; + +const zoomToNodes = (chart: ForceGraphMethods, nodes: any[]) => { + if (!chart) { + console.warn('Chart reference is not available'); + return; + } + chart.zoomToFit(ZOOM_DURATION, ZOOM_PADDING, (n: NodeObject<Node>) => + nodes.some(node => node.id === n.id) + ); +}; const handleSubmit = async () => { const chart = chartRef.current if (!chart) return // ... rest of the function ... - setTimeout(() => { - chart.zoomToFit(1000, 150, (n: NodeObject<Node>) => - formattedPaths.some(p => p.nodes.some(node => node.id === n.id)) - ); - }, 0) + zoomToNodes(chart, formattedPaths.flatMap(p => p.nodes)); } const handleSetSelectedPath = (p: PathData) => { // ... rest of the function ... - setTimeout(() => { - chart.zoomToFit(1000, 150, (n: NodeObject<Node>) => - p.nodes.some(node => node.id === n.id) - ); - }, 0) + zoomToNodes(chart, p.nodes); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
app/components/chat.tsx(9 hunks)app/components/code-graph.tsx(5 hunks)app/components/elementMenu.tsx(3 hunks)app/components/graphView.tsx(5 hunks)app/components/model.ts(3 hunks)app/page.tsx(1 hunks)e2e/logic/POM/codeGraph.ts(4 hunks)e2e/tests/canvas.spec.ts(1 hunks)e2e/tests/chat.spec.ts(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: build
- GitHub Check: test
- GitHub Check: test
🔇 Additional comments (11)
e2e/tests/chat.spec.ts (1)
120-121: LGTM! Good test isolation.Moving the API calls inside the test scope improves test isolation and follows testing best practices.
app/components/elementMenu.tsx (1)
17-17: LGTM! Consistent function naming.The typo fix from
handelExpandtohandleExpandis applied consistently across the component.Also applies to: 22-22, 71-71, 77-77, 117-117, 123-123
app/components/model.ts (1)
55-55: LGTM! Safe default parameter.Adding a default value of 0 ensures the function always returns a valid color.
app/page.tsx (1)
284-284: LGTM! Enhanced graph interaction.Adding the chartRef prop to the Chat component enables improved graph visualization capabilities.
e2e/tests/canvas.spec.ts (1)
46-46: LGTM! Good improvement to test reliability.Adding
clickCenter()before zoom operations ensures consistent test behavior by establishing a known starting position.app/components/graphView.tsx (2)
64-87: Good implementation of responsive graph sizing.The resize observer implementation properly handles both window resizing and parent container changes. The cleanup in the
useEffecthook correctly removes event listeners and disconnects the observer.
173-174: LGTM! Proper usage of dynamic dimensions.Using state-managed dimensions ensures the graph properly responds to container size changes.
app/components/code-graph.tsx (1)
Line range hint
244-357: LGTM! Good cleanup of handler names.The renaming from 'handel*' to 'handle*' improves code consistency and fixes typos.
e2e/logic/POM/codeGraph.ts (2)
155-157: LGTM! Good addition of loading state locator.The new locator helps track loading states consistently across tests.
426-426: Verify the impact of changing to right-click.The change from left-click to right-click for node selection might affect other tests that depend on this behavior.
Let's verify the impact:
✅ Verification successful
Right-click change appears safe to implement
The tests use the
nodeClick()abstraction method rather than direct click events, focusing on outcomes rather than the click mechanism. The change from left to right click is an implementation detail that won't affect the test behavior as long as thenodeClick()method maintains its core functionality of node selection.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for tests that might be affected by the click behavior change rg -l "nodeClick" "e2e/tests"Length of output: 87
Script:
#!/bin/bash # Check the content of test files for click-related code echo "=== canvas.spec.ts ===" rg -A 5 -B 5 "click" "e2e/tests/canvas.spec.ts" echo -e "\n=== nodeDetailsPanel.spec.ts ===" rg -A 5 -B 5 "click" "e2e/tests/nodeDetailsPanel.spec.ts"Length of output: 8469
app/components/chat.tsx (1)
110-110: LGTM!The function signature has been correctly updated to include the new
chartRefprop.
… handling and neighbor deletion logic. Updated deleteNeighbors function to handle expanded nodes correctly and replaced direct calls with handleExpand in GraphView for better clarity and maintainability.
There was a problem hiding this comment.
Actionable comments posted: 1
🔭 Outside diff range comments (1)
app/components/Input.tsx (1)
Line range hint
201-201: Warning: Dynamic Tailwind class names may not work in production.The dynamic class name
bg-${colorName}might not survive Tailwind's purge in production. Consider using style objects or safelist these color classes in your Tailwind config.Example safelist configuration in
tailwind.config.js:module.exports = { safelist: [ { pattern: /bg-(red|blue|green|yellow|purple|pink|indigo|gray)-/, variants: ['hover', 'focus', 'active'] } ] }
🧹 Nitpick comments (1)
app/components/Input.tsx (1)
Line range hint
147-160: Consider extracting input validation logic.While the validation works correctly, consider extracting it into a reusable function for better maintainability.
+const INVALID_CHARS_PATTERN = /[%*()\-\[\]{};:"|~]/; +const getInvalidCharacterMessage = (char: string) => + `The character "${char}" is not allowed in this field.`; + +const validateInput = (value: string): { isValid: boolean; message: string } => { + const match = value.match(INVALID_CHARS_PATTERN); + return match + ? { isValid: false, message: getInvalidCharacterMessage(match[0]) } + : { isValid: true, message: '' }; +}; + onChange={(e) => { const newVal = e.target.value - const invalidChars = /[%*()\-\[\]{};:"|~]/; - - if (invalidChars.test(newVal)) { - e.target.setCustomValidity(`The character "${newVal.match(invalidChars)?.[0]}" is not allowed in this field.`); + const { isValid, message } = validateInput(newVal); + if (!isValid) { + e.target.setCustomValidity(message); e.target.reportValidity(); return; } e.target.setCustomValidity(''); onValueChange({ name: newVal }) }}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
app/components/Input.tsx(6 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
- GitHub Check: test
- GitHub Check: test
🔇 Additional comments (2)
app/components/Input.tsx (2)
18-18: LGTM! Good architectural improvement.The removal of the
valueprop in favor of usingnode?.namemakes the component more focused and reduces prop drilling.
Line range hint
48-79: LGTM! Well-implemented search functionality.The implementation includes:
- Proper debouncing with timeout
- Clean error handling with user feedback
- Appropriate cleanup in useEffect
…t handlers for improved link and node interactions
…ode-graph into merge-main-to-staging
…ode-graph into merge-main-to-staging
Update dependencies and improve test configurations
There was a problem hiding this comment.
Actionable comments posted: 17
🧹 Nitpick comments (9)
.github/dependabot.yml (1)
13-16: Group name suggests minor updates but only patches are included.The group is named
npm-minor-patchbut only includes"patch"inupdate-types. This naming could be confusing. Consider either:
- Renaming to
npm-patchif only patch updates are intended, or- Adding
"minor"toupdate-typesif minor updates should also be grouped.💡 Option 1: Rename to match behavior
groups: - npm-minor-patch: + npm-patch: update-types: - "patch"💡 Option 2: Include minor updates
groups: npm-minor-patch: update-types: + - "minor" - "patch"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/dependabot.yml around lines 13 - 16, The dependabot group name npm-minor-patch does not match its update-types (only "patch"); either rename the group to npm-patch or add "minor" to update-types to match intent—update the groups -> npm-minor-patch name or modify the update-types array (the keys involved are groups, npm-minor-patch and update-types) accordingly so the config and naming are consistent.e2e/logic/utils.ts (2)
52-54: Consider adding type annotations for improved type safety.The function signature uses
any[]and returnsany, which reduces type safety. If the node structure is known, consider using a more specific type or generic.💡 Optional: Type-safe alternative
-export function findNodeByName(nodes: any[], nodeName: string): any { +export function findNodeByName<T extends { name?: string; data?: { name?: string } }>( + nodes: T[], + nodeName: string +): T | undefined { return nodes.find((node) => node.name === nodeName || node.data?.name === nodeName); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@e2e/logic/utils.ts` around lines 52 - 54, The findNodeByName function currently uses any[] and any; replace these with a type-safe signature by introducing a Node-like shape or a generic: define or use an interface with optional fields name?: string and data?: { name?: string } and change findNodeByName to accept nodes of that type (or a generic T extends that shape) and return T | undefined instead of any; update any callsites if necessary to satisfy the new return type.
16-35: Consider edge case: function returns potentially unstable text.The function returns
stableTextat the end even if the text never stabilized (i.e., it kept changing until timeout). This might be the intended behavior, but consider throwing an error or logging a warning when timeout is reached without stabilization to help debug flaky tests.💡 Optional: Add timeout warning
for (let i = 0; i < maxChecks; i++) { stableText = await locator.textContent() ?? ""; if (stableText === previousText && stableText.trim().length > 0) { return stableText; } previousText = stableText; await locator.page().waitForTimeout(pollingInterval); } + console.warn(`Text did not stabilize within ${timeout}ms, returning last value`); return stableText; };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@e2e/logic/utils.ts` around lines 16 - 35, waitForStableText currently returns the last-read stableText even if the loop timed out without stabilization; update the end of waitForStableText to treat timeout as a failure: throw an Error (or at minimum log a warning) indicating the text never stabilized, include contextual info (the last observed stableText, locator description or string, and the timeout value) so tests can fail fast and debugging is easier; locate this logic in the waitForStableText function and replace the final return stableText with a thrown error or a process/test logger call as appropriate for the test runner.package.json (1)
29-29: Add@types/d3to devDependencies for TypeScript support.While
d3@^7.9.0does not include built-in TypeScript declarations,d3is not currently imported in the codebase. However, since the library is now a dependency, adding types ahead of time will prevent TypeScript compilation issues when it's used. Install via DefinitelyTyped:"@types/d3": "^7.4.3"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@package.json` at line 29, Add the DefinitelyTyped declarations for D3 by adding "@types/d3": "^7.4.3" to devDependencies in package.json so TypeScript has typings when d3 is imported; update the package.json devDependencies block (referencing the existing "d3" dependency) and run npm/yarn install to persist the change.lib/utils.ts (3)
2-2: Unused import:GraphNode.
GraphNodeis imported from@falkordb/canvasbut is not used anywhere in this file.♻️ Proposed fix
-import FalkorDBCanvas, { GraphNode } from "@falkordb/canvas" +import FalkorDBCanvas from "@falkordb/canvas"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/utils.ts` at line 2, The import includes an unused symbol GraphNode from `@falkordb/canvas`; remove GraphNode from the import statement (leave FalkorDBCanvas) to eliminate the unused import warning and keep imports minimal.
7-10: Consider stronger typing instead ofany[].Using
any[]fornodesandlinksreduces type safety. Consider using the importedNodeandLinktypes for better compile-time checks.♻️ Proposed fix
+import { Link, Node } from "@/app/components/model" + export type PathData = { - nodes: any[] - links: any[] + nodes: Node[] + links: Link[] }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/utils.ts` around lines 7 - 10, The PathData type currently uses any[] for nodes and links which reduces type safety; update the PathData definition in lib/utils.ts to use the specific imported Node and Link types (i.e., nodes: Node[] and links: Link[]), add/import those types if they are not already imported, and adjust any call sites or tests that relied on any[] to satisfy the stronger types.
31-36: Consider stronger typing forpathsproperty.The
pathsproperty usesany[]for both nodes and links. This could leverage thePathDatatype defined above for consistency and type safety.♻️ Proposed fix
export interface Message { type: MessageTypes; text?: string; - paths?: { nodes: any[], links: any[] }[]; + paths?: PathData[]; graphName?: string; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/utils.ts` around lines 31 - 36, The Message interface's paths property is weakly typed using any[]; replace it with the existing PathData type for stronger typing: change paths?: { nodes: any[], links: any[] }[] to paths?: PathData[] (or Array<PathData>) in the Message interface so it uses the PathData definition above and preserves optionality. Ensure PathData is visible in the same module (or imported) so the Message type compiles.app/components/toolbar.tsx (1)
64-64: Inconsistent title casing.The
titleattribute uses camelCase"downloadImage"while other buttons use title case like"Zoom In","Zoom Out", and"Center". Consider using consistent casing for accessibility and consistency.♻️ Proposed fix
<button className="hidden md:block control-button" - title="downloadImage" + title="Download Image" onClick={handleDownloadImage} >🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/components/toolbar.tsx` at line 64, The title attribute for the download button ("downloadImage") is using camelCase while other buttons use Title Case, causing inconsistent accessibility labels; update the title attribute in the toolbar component (the element with title="downloadImage") to use Title Case e.g. "Download Image" to match "Zoom In"/"Zoom Out"/"Center" and ensure consistency across the toolbar UI.app/api/repo/route.ts (1)
45-45: Hardcoded ignore list may not be appropriate for all repositories.The
ignorearray is hardcoded with specific directory patterns. This may not apply universally to all repositories being analyzed. Consider making this configurable via environment variables or request parameters.♻️ Proposed configuration approach
+const DEFAULT_IGNORE = ["./.github", "./sbin", "./.git", "./deps", "./bin", "./build"]; + export async function POST(request: NextRequest) { const repo_url = request.nextUrl.searchParams.get('url'); + const customIgnore = request.nextUrl.searchParams.get('ignore'); + const ignore = customIgnore ? JSON.parse(customIgnore) : DEFAULT_IGNORE; // ... const result = await fetch(`${url}/${isLocal ? "analyze_folder" : "analyze_repo"}`, { method: 'POST', - body: JSON.stringify({ repo_url, ignore: ["./.github", "./sbin", "./.git", "./deps", "./bin", "./build"] }), + body: JSON.stringify({ repo_url, ignore }),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/api/repo/route.ts` at line 45, The hardcoded ignore array in the request body (body: JSON.stringify({ repo_url, ignore: [...] })) should be made configurable: update the handler in app/api/repo/route.ts (the function that constructs this body) to accept an optional ignore list from the incoming request payload (e.g., request.body.ignore) and/or read a fallback from an environment variable (e.g., REPO_IGNORE_LIST as JSON or comma-separated); if neither is provided, fall back to the current default array. Validate/parses the env or request value into an array before including it in JSON.stringify so existing behavior remains unchanged when no config is supplied.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/components/chat.tsx`:
- Line 100: The overlap check uses the same identifier for both elements so e.id
=== e.id is always true; update the inner some() callback to use a different
variable name (e.g., prevElem) and compare e.id === prevElem.id so you're
comparing an element from the current path (paths.some path -> e) against
elements from the previous selection ([...prev.nodes, ...prev.links] ->
prevElem). Locate the lambda in the paths.some(... every(... some(...)))
expression in chat.tsx and rename the inner callback parameter and its
comparison to fix the logic.
In `@app/components/code-graph.tsx`:
- Around line 106-110: The Delete-key guard in handleKeyDown is inverted: it
currently returns early when selectedObj exists and selectedObjects is empty,
preventing single-selection deletion; update the condition so it only returns
early when there is neither a single selectedObj nor any selectedObjects (i.e.,
if (!selectedObj && selectedObjects.length === 0) return), then call
handleRemove with the combined ids computed from selectedObjects.map(obj =>
obj.id) and selectedObj?.id filtered for undefined, ensuring you still pass
"nodes" as the second arg.
In `@app/components/dataPanel.tsx`:
- Around line 21-26: The excludedProperties array in dataPanel.tsx contains a
duplicate entry "isPathSelected"; remove the redundant "isPathSelected" so that
excludedProperties only lists each property once (locate the array named
excludedProperties in app/components/dataPanel.tsx and delete the duplicate
string entry).
- Around line 95-112: The valueRenderer is declared with parameters
(valueAsString, value, keyPath) but react-json-tree passes keyPath as rest
parameters; update the valueRenderer implementation (the prop named
valueRenderer) to accept rest parameters (e.g., (...keyPath)) and then check the
first element keyPath[0] === "src" to decide when to render the
SyntaxHighlighter in the DataPanel component; ensure the fallback still returns
the span with className "text-white".
In `@app/components/graphView.tsx`:
- Around line 167-168: The current checks use "!node.x || !node.y" which
incorrectly treats 0 as missing; update both occurrences where that check
appears (the conditional guarding rendering/hit-testing of a node) to explicitly
test for null/undefined instead, e.g. use "node.x == null || node.y == null" or
"node.x === undefined || node.y === undefined" (or prefer
Number.isFinite(node.x) && Number.isFinite(node.y) if you want to ensure numeric
coordinates) so nodes at x=0 or y=0 are not skipped.
In `@app/components/model.ts`:
- Around line 197-230: When synthesizing missing endpoint nodes (the local
variables source and target created from edgeData.src_node / edgeData.dest_node)
you currently only set them in this.nodesMap; also append the newly created node
objects to the exported element lists (e.g. this.elements.nodes and, if present
in context, newElements.nodes) so links reference actual exported nodes; update
the blocks that create source and target to push the created node into
this.elements.nodes (and newElements.nodes when used) immediately after
this.nodesMap.set(...) to keep maps and element arrays in sync.
In `@app/page.tsx`:
- Around line 371-388: External links rendered with the Link components (e.g.,
the Link instances that render the FalkorDB logo, "Main Website", "Github",
"Discord" etc. in app/page.tsx) use target='_blank' but lack rel='noopener
noreferrer'; update each external Link element that opens in a new tab to
include rel="noopener noreferrer" alongside target='_blank' to prevent
tabnabbing and improve security—ensure every Link with target='_blank' (all
occurrences around the header block and lines ~537-560) is amended accordingly.
- Line 617: The mobile CodeGraph is calling onCategoryClick with the desktop
canvas ref (desktopChartRef) so mobile category toggles target the wrong canvas;
update the mobile CodeGraph invocation to pass the mobile canvas ref (e.g.,
mobileChartRef or the ref used by the mobile CodeGraph instance) into
onCategoryClick instead of desktopChartRef, ensuring the onCategoryClick(name,
show, ...) call for the mobile component uses the mobile ref; search for the
CodeGraph mobile render and replace the passed desktopChartRef with the
mobileChartRef symbol (or the precise ref variable name used for mobile).
In `@e2e/logic/POM/codeGraph.ts`:
- Line 152: The XPath in locateNodeInLastChatPath is invalid because ${node} is
injected unquoted; wrap the node string in quotes and escape any internal quotes
— e.g., interpolate a properly quoted JS string using JSON.stringify(node) so
the XPath becomes contains(text(), ${JSON.stringify(node)}); update the locator
returned by locateNodeInLastChatPath accordingly to ensure safe, valid XPath.
- Around line 595-605: The function always calls window.graphDesktop() which
returns the wrong instance in mobile tests; update the accessor selection so
that both the waitForFunction check and the final page.evaluate call use
window.graphMobile() when running in mobile mode and window.graphDesktop()
otherwise—detect mobile via the Playwright context/page mode available in this
class (e.g., this.page.isMobile or other environment flag) and replace the
hardcoded graphDesktop references (graphDesktop, graphMobile, page.evaluate,
page.waitForFunction) so the code conditionally invokes the correct window graph
accessor before awaiting canvas animation and returning the graph data.
In `@e2e/tests/canvas.spec.ts`:
- Around line 222-225: Replace unsafe non-null assertions on firstNodeRes and
secondNodeRes when calling api.showPath and in expectations: after asserting
firstNodeRes and secondNodeRes are defined (using expect(...).toBeDefined()),
extract their ids with a guard (e.g., throw or return if undefined) or use
optional chaining when accessing .id, and use those guarded ids in the
api.showPath call and subsequent checks for callsRelationObject (from
response.result.paths[0].find). Also guard callsRelationObject before asserting
src_node/dest_node to avoid runtime errors if the relation is missing.
In `@e2e/tests/chat.spec.ts`:
- Around line 85-87: The test uses a non-null assertion on const number =
uiResponse.match(/\d+/g)?.[0]! which can throw if the regex doesn't match and
also directly compares to apiResponse.result.response.match(...), so update the
assertion to avoid the trailing "!" and first capture both matches into
temporaries (e.g., uiMatch and apiMatch), assert each is defined (or fail with a
clear message) and then compare them; reference the variables uiResponse,
apiResponse, and the extracted value currently named number to locate the lines
to change and ensure robust null/undefined handling before calling
expect(...).toEqual(...).
- Line 69: The line "const number = result.match(/\d+/g)?.[0]!" uses a non-null
assertion after optional chaining which is unsafe; change it to explicitly
handle the possible null/undefined match: call result.match(/\d+/g) into a local
variable (e.g., const match = result.match(/\d+/g)), assert or throw if match is
null or match[0] is falsy (or use an expect(...) assertion in the test), then
assign const number = match[0]; this removes the "!.", ensures deterministic
failures with a clear error message, and references the existing identifiers
"number" and the result.match(...) expression in chat.spec.ts.
In `@package.json`:
- Line 33: package.json pins Next.js to "next": "^15.5.8" which has security
advisories and may require React 19 for App Router; update the "next" dependency
to a patched release (>=15.5.9) and, if your app uses the App Router, bump
"react" and "react-dom" to React 19-compatible versions (replace current "react"
and "react-dom" entries) and run install and test to resolve peer dependency
warnings and verify the app and App Router behavior.
In `@playwright.config.ts`:
- Around line 22-23: The comment above the workers setting is stale: update the
comment to accurately describe the current behavior of the workers property
(workers: process.env.CI ? 3 : undefined) — e.g., change the comment to
something like "Use a fixed 3 workers on CI (otherwise default)" or similar so
it no longer says we opt out of parallel tests; ensure you edit the comment
immediately above the workers property that references the workers setting.
In `@README.md`:
- Line 109: Update the GitHub Issues link in the README: replace the current URL
"https://github.com/FalkorDB/GraphRAG-SDK/issues" referenced by the "[GitHub
Issues]" list item with "https://github.com/FalkorDB/code-graph/issues" so the
repo's README points to the correct issue tracker for code-graph.
- Line 9: Remove the stray lone "-" character present in the README (the stray
hyphen on its own line) so the file has no extraneous characters; simply delete
that line containing only "-" and save the README to restore clean formatting.
---
Nitpick comments:
In @.github/dependabot.yml:
- Around line 13-16: The dependabot group name npm-minor-patch does not match
its update-types (only "patch"); either rename the group to npm-patch or add
"minor" to update-types to match intent—update the groups -> npm-minor-patch
name or modify the update-types array (the keys involved are groups,
npm-minor-patch and update-types) accordingly so the config and naming are
consistent.
In `@app/api/repo/route.ts`:
- Line 45: The hardcoded ignore array in the request body (body:
JSON.stringify({ repo_url, ignore: [...] })) should be made configurable: update
the handler in app/api/repo/route.ts (the function that constructs this body) to
accept an optional ignore list from the incoming request payload (e.g.,
request.body.ignore) and/or read a fallback from an environment variable (e.g.,
REPO_IGNORE_LIST as JSON or comma-separated); if neither is provided, fall back
to the current default array. Validate/parses the env or request value into an
array before including it in JSON.stringify so existing behavior remains
unchanged when no config is supplied.
In `@app/components/toolbar.tsx`:
- Line 64: The title attribute for the download button ("downloadImage") is
using camelCase while other buttons use Title Case, causing inconsistent
accessibility labels; update the title attribute in the toolbar component (the
element with title="downloadImage") to use Title Case e.g. "Download Image" to
match "Zoom In"/"Zoom Out"/"Center" and ensure consistency across the toolbar
UI.
In `@e2e/logic/utils.ts`:
- Around line 52-54: The findNodeByName function currently uses any[] and any;
replace these with a type-safe signature by introducing a Node-like shape or a
generic: define or use an interface with optional fields name?: string and
data?: { name?: string } and change findNodeByName to accept nodes of that type
(or a generic T extends that shape) and return T | undefined instead of any;
update any callsites if necessary to satisfy the new return type.
- Around line 16-35: waitForStableText currently returns the last-read
stableText even if the loop timed out without stabilization; update the end of
waitForStableText to treat timeout as a failure: throw an Error (or at minimum
log a warning) indicating the text never stabilized, include contextual info
(the last observed stableText, locator description or string, and the timeout
value) so tests can fail fast and debugging is easier; locate this logic in the
waitForStableText function and replace the final return stableText with a thrown
error or a process/test logger call as appropriate for the test runner.
In `@lib/utils.ts`:
- Line 2: The import includes an unused symbol GraphNode from `@falkordb/canvas`;
remove GraphNode from the import statement (leave FalkorDBCanvas) to eliminate
the unused import warning and keep imports minimal.
- Around line 7-10: The PathData type currently uses any[] for nodes and links
which reduces type safety; update the PathData definition in lib/utils.ts to use
the specific imported Node and Link types (i.e., nodes: Node[] and links:
Link[]), add/import those types if they are not already imported, and adjust any
call sites or tests that relied on any[] to satisfy the stronger types.
- Around line 31-36: The Message interface's paths property is weakly typed
using any[]; replace it with the existing PathData type for stronger typing:
change paths?: { nodes: any[], links: any[] }[] to paths?: PathData[] (or
Array<PathData>) in the Message interface so it uses the PathData definition
above and preserves optionality. Ensure PathData is visible in the same module
(or imported) so the Message type compiles.
In `@package.json`:
- Line 29: Add the DefinitelyTyped declarations for D3 by adding "@types/d3":
"^7.4.3" to devDependencies in package.json so TypeScript has typings when d3 is
imported; update the package.json devDependencies block (referencing the
existing "d3" dependency) and run npm/yarn install to persist the change.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (28)
.github/dependabot.yml.github/workflows/nextjs.yml.github/workflows/playwright.ymlDockerfileREADME.mdapp/api/repo/route.tsapp/components/ForceGraph.tsxapp/components/chat.tsxapp/components/code-graph.tsxapp/components/dataPanel.tsxapp/components/elementMenu.tsxapp/components/graphView.tsxapp/components/model.tsapp/components/toolbar.tsxapp/page.tsxcomponents/ui/switch.tsxe2e/config/testData.tse2e/infra/ui/basePage.tse2e/infra/ui/browserWrapper.tse2e/logic/POM/codeGraph.tse2e/logic/utils.tse2e/tests/canvas.spec.tse2e/tests/chat.spec.tse2e/tests/nodeDetailsPanel.spec.tse2e/tests/searchBar.spec.tslib/utils.tspackage.jsonplaywright.config.ts
✅ Files skipped from review due to trivial changes (1)
- e2e/infra/ui/browserWrapper.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- .github/workflows/playwright.yml
- app/components/elementMenu.tsx
- e2e/tests/nodeDetailsPanel.spec.ts
|
|
||
| Have questions or feedback? Reach out via: | ||
|
|
||
| * [GitHub Issues](https://github.com/FalkorDB/GraphRAG-SDK/issues) |
There was a problem hiding this comment.
Verify the GitHub Issues link.
The issues link points to https://github.com/FalkorDB/GraphRAG-SDK/issues, but this README is for the code-graph repository. Should this link to https://github.com/FalkorDB/code-graph/issues instead?
🔧 Proposed fix
-* [GitHub Issues](https://github.com/FalkorDB/GraphRAG-SDK/issues)
+* [GitHub Issues](https://github.com/FalkorDB/code-graph/issues)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| * [GitHub Issues](https://github.com/FalkorDB/GraphRAG-SDK/issues) | |
| * [GitHub Issues](https://github.com/FalkorDB/code-graph/issues) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@README.md` at line 109, Update the GitHub Issues link in the README: replace
the current URL "https://github.com/FalkorDB/GraphRAG-SDK/issues" referenced by
the "[GitHub Issues]" list item with
"https://github.com/FalkorDB/code-graph/issues" so the repo's README points to
the correct issue tracker for code-graph.
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
There was a problem hiding this comment.
Actionable comments posted: 11
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/components/chat.tsx (1)
64-69:⚠️ Potential issue | 🟡 MinorMissing
pathsin useEffect dependency array.The effect accesses
pathsto find the selected path, butpathsis not in the dependency array. This could cause stale closure issues where updates topathsdon't trigger re-evaluation.Proposed fix
useEffect(() => { const p = paths.find((path) => [...path.links, ...path.nodes].some((e: any) => e.id === selectedPathId)) if (!p) return handleSetSelectedPath(p) - }, [selectedPathId]) + }, [selectedPathId, paths])Note: You may also need to add
handleSetSelectedPathto the dependency array or wrap it inuseCallback.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/components/chat.tsx` around lines 64 - 69, The effect using useEffect reads `paths` to find the selected path but only lists `selectedPathId` in its dependency array, causing stale closures; update the dependency array for that useEffect to include `paths` (and also include or memoize `handleSetSelectedPath`) so the effect re-runs whenever `paths` or the setter change; alternatively wrap `handleSetSelectedPath` in useCallback and include it in the dependency list to avoid unnecessary re-renders.
♻️ Duplicate comments (9)
playwright.config.ts (1)
22-23:⚠️ Potential issue | 🟡 MinorUpdate the stale CI parallelism comment.
Line 22 says CI opts out of parallel tests, but Line 23 explicitly sets 3 workers on CI.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@playwright.config.ts` around lines 22 - 23, The comment above the Playwright config is stale: it claims CI opts out of parallel tests but the workers property explicitly sets workers: process.env.CI ? 3 : undefined. Fix by making them consistent: either change the comment to state that CI runs with 3 workers (e.g., "Use 3 workers on CI") or change the code to opt out (set workers: process.env.CI ? undefined : undefined or workers: process.env.CI ? 1 : undefined) depending on intent; update the comment near the workers property to accurately describe the behavior of the workers setting.README.md (2)
109-109:⚠️ Potential issue | 🟡 MinorFix the GitHub Issues link.
The link points to
GraphRAG-SDKbut this README is for thecode-graphrepository.🔧 Proposed fix
-* [GitHub Issues](https://github.com/FalkorDB/GraphRAG-SDK/issues) +* [GitHub Issues](https://github.com/FalkorDB/code-graph/issues)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@README.md` at line 109, Update the GitHub Issues link in README.md: replace the existing link text "[GitHub Issues](https://github.com/FalkorDB/GraphRAG-SDK/issues)" so it points to the code-graph repository issues URL (e.g., https://github.com/FalkorDB/code-graph/issues), ensuring the "[GitHub Issues]" entry now references the correct repository; keep the link label unchanged.
9-9:⚠️ Potential issue | 🟡 MinorRemove stray character.
Line 9 contains a lone
-which appears to be a leftover from editing.🔧 Proposed fix
-🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@README.md` at line 9, Remove the stray lone '-' character found in the README (the isolated dash on line 9) by deleting that character so the file contains only intended content; ensure no other stray punctuation remains and run a quick preview to confirm formatting is unchanged.app/globals.css (1)
132-139:⚠️ Potential issue | 🟡 MinorRemove duplicate
@layer basedeclaration.This block duplicates the
@layer basedeclaration at lines 85-94. Additionally, line 137 missesfont-robertothat exists in the original at line 91.🔧 Proposed fix
-@layer base { - * { - `@apply` border-border; - } - body { - `@apply` bg-background text-foreground; - } -}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/globals.css` around lines 132 - 139, Remove the duplicate `@layer` base block and merge its rules into the existing `@layer` base (avoid repeating the `@layer` base declaration); specifically ensure the body selector inside the retained `@layer` base includes the missing font utility by adding the font-roberto utility to the existing body `@apply` (so body uses bg-background text-foreground and font-roberto) and keep the universal * { `@apply` border-border } rule in that single `@layer` base.app/components/dataPanel.tsx (1)
21-27:⚠️ Potential issue | 🟡 MinorRemove duplicated
isPathSelectedfromexcludedProperties.The same key appears twice, which is redundant and easy to miss in future edits.
Suggested fix
"curve", "__indexColor", - "isPathSelected", "__controlPoints",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/components/dataPanel.tsx` around lines 21 - 27, The excludedProperties array contains a duplicate string "isPathSelected"; remove the redundant "isPathSelected" entry from the excludedProperties definition (leave a single occurrence) to avoid redundancy and potential confusion when editing, locating the array by the symbol name excludedProperties in dataPanel.tsx and updating that list accordingly.e2e/tests/canvas.spec.ts (1)
222-225:⚠️ Potential issue | 🟡 MinorRemove unsafe non-null assertions before API/path assertions.
Lines 222, 224, and 225 still rely on
!for node ids; this was already flagged and remains brittle.Suggested fix
expect(firstNodeRes).toBeDefined(); expect(secondNodeRes).toBeDefined(); + if (!firstNodeRes || !secondNodeRes) throw new Error("Path nodes were not found in graph payload"); const api = new ApiCalls(); - const response = await api.showPath(GRAPHRAG_SDK ,firstNodeRes!.id, secondNodeRes!.id); + const response = await api.showPath(GRAPHRAG_SDK, firstNodeRes.id, secondNodeRes.id); const callsRelationObject = response.result.paths[0].find(item => item.relation === "CALLS") - expect(callsRelationObject?.src_node).toBe(firstNodeRes!.id); - expect(callsRelationObject?.dest_node).toBe(secondNodeRes!.id); + expect(callsRelationObject?.src_node).toBe(firstNodeRes.id); + expect(callsRelationObject?.dest_node).toBe(secondNodeRes.id);#!/bin/bash # Verify remaining non-null id assertions in canvas tests rg -nP '!\.id' e2e/tests/canvas.spec.ts🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@e2e/tests/canvas.spec.ts` around lines 222 - 225, Replace unsafe non-null assertions by asserting presence and guarding before accessing ids: before calling api.showPath(GRAPHRAG_SDK, firstNodeRes!.id, secondNodeRes!.id) ensure firstNodeRes and secondNodeRes are defined (e.g., expect(firstNodeRes).toBeDefined(); expect(secondNodeRes).toBeDefined()) and then call showPath with their .id without using `!`; after getting response, assert response.result.paths exists and has at least one path, find callsRelationObject, and assert callsRelationObject is defined before checking callsRelationObject.src_node and callsRelationObject.dest_node (use expect(callsRelationObject).toBeDefined(); expect(callsRelationObject!.src_node).toBe(firstNodeRes.id); etc.).e2e/tests/chat.spec.ts (1)
69-87:⚠️ Potential issue | 🟡 MinorAvoid non-null assertions after optional chaining in regex extraction.
Lines 69 and 85 can fail with unclear errors when the regex does not match.
Suggested fix
const result = await chat.getTextInLastChatElement(); - const number = result.match(/\d+/g)?.[0]!; + const match = result.match(/\d+/g); + expect(match?.[0]).toBeDefined(); + const number = match![0]; @@ const uiResponse = await chat.getTextInLastChatElement(); - const number = uiResponse.match(/\d+/g)?.[0]!; - - expect(number).toEqual(apiResponse.result.response.match(/\d+/g)?.[0]); + const uiMatch = uiResponse.match(/\d+/g); + const apiMatch = apiResponse.result.response.match(/\d+/g); + expect(uiMatch?.[0]).toBeDefined(); + expect(apiMatch?.[0]).toBeDefined(); + expect(uiMatch![0]).toEqual(apiMatch![0]);#!/bin/bash # Verify remaining optional-chain non-null assertions in this file rg -nP '\.match\(/\\d\+\/g\)\?\.\[0\]!' e2e/tests/chat.spec.ts🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@e2e/tests/chat.spec.ts` around lines 69 - 87, The regex extractions using optional chaining plus non-null assertion (e.g., uiResponse.match(/\d+/g)?.[0]! and apiResponse.result.response.match(/\d+/g)?.[0]!) can throw unclear runtime errors when there's no match; update the test to safely handle missing matches by removing the non-null assertions and checking that the match exists before using it: capture the match arrays (from uiResponse.match and apiResponse.result.response.match), assert they are defined/contain at least one element (or fail the test with a clear message) and then compare the first element; look for the variables uiResponse, apiResponse, number and the test "Validate UI response matches API response for a given question in chat" to apply this change.package.json (1)
33-33:⚠️ Potential issue | 🟠 Major
nextversion concern appears unresolved from prior review.Line 33 still targets
^15.5.8, which was previously flagged for advisories. Please move to the first patched 15.x version (or newer supported line).Check official Next.js advisories/release notes for next@15.5.8 and identify the first patched 15.x version that resolves those advisories.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@package.json` at line 33, Update the "next" dependency in package.json (the "next" entry currently set to "^15.5.8") to the first patched 15.x release that resolves the flagged advisories (or to a newer supported release line if preferred); consult Next.js release notes/security advisories to identify that exact patched 15.x version and replace the version string accordingly, then run your lockfile update (npm/yarn/pnpm) and verify the new version is installed and tests/builds succeed.app/components/code-graph.tsx (1)
106-110:⚠️ Potential issue | 🟠 MajorFix Delete-key handling for single selection and link deletion.
Line 108 is still inverted, and Line 109 always deletes as
"nodes"even whenselectedObjis aLink. This blocks valid single-delete flows and can target the wrong entity type.Proposed fix
const handleKeyDown = (event: KeyboardEvent) => { if (event.key === 'Delete') { - if (selectedObj && selectedObjects.length === 0) return - handleRemove([...selectedObjects.map(obj => obj.id), selectedObj?.id].filter(id => id !== undefined), "nodes"); + if (!selectedObj && selectedObjects.length === 0) return + + const isSelectedObjLink = !!selectedObj && "source" in selectedObj + if (isSelectedObjLink && selectedObjects.length === 0) { + handleRemove([selectedObj.id], "links") + return + } + + const nodeIds = [ + ...selectedObjects.map(obj => obj.id), + ...(!isSelectedObjLink && selectedObj ? [selectedObj.id] : []), + ] + if (nodeIds.length === 0) return + handleRemove(nodeIds, "nodes") } };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/components/code-graph.tsx` around lines 106 - 110, The Delete-key handler in handleKeyDown incorrectly returns when a single selection exists and always calls handleRemove with "nodes"; invert the early-return check so it returns only when there is no selection (i.e., if (!selectedObj && selectedObjects.length === 0) return), and determine the correct removal type by inspecting the selected item(s): if selectedObj?.type === "Link" (or instanceof Link depending on your model) call handleRemove(..., "links"), otherwise call handleRemove(..., "nodes"); when mixing selections, compute ids and types accordingly (e.g., map selectedObjects to their ids and ensure you pass the appropriate entity type rather than hardcoding "nodes").
🟡 Minor comments (12)
.github/dependabot.yml-13-16 (1)
13-16:⚠️ Potential issue | 🟡 MinorGroup name doesn't match the configured update types.
The group is named
npm-minor-patch, suggesting it should include both minor and patch updates, but onlypatchis specified inupdate-types. Either:
- Add
- "minor"if the intent is to group both minor and patch updates, or- Rename the group to
npm-patchto accurately reflect its scope.Option A: Include minor updates
groups: npm-minor-patch: update-types: - "patch" + - "minor"Option B: Rename group to match scope
groups: - npm-minor-patch: + npm-patch: update-types: - "patch"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/dependabot.yml around lines 13 - 16, The group name "npm-minor-patch" in the dependabot configuration doesn't match its configured update-types; either add "- \"minor\"" to the update-types list to include minor and patch updates, or rename the group to "npm-patch" so the group name reflects that only patch updates are included—update the groups entry for npm-minor-patch and the update-types key accordingly.components/ui/carousel.tsx-109-121 (1)
109-121:⚠️ Potential issue | 🟡 MinorMissing cleanup for
reInitevent listener.The effect adds listeners for both
reInitandselectevents, but the cleanup only removes theselectlistener. This could cause a memory leak.🔧 Proposed fix
return () => { + api?.off("reInit", onSelect) api?.off("select", onSelect) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/ui/carousel.tsx` around lines 109 - 121, The effect registers both "reInit" and "select" listeners on the carousel API but only removes "select" on cleanup; update the cleanup inside the React.useEffect that references api and onSelect to also call api?.off("reInit", onSelect) (i.e., remove both listeners) so both event handlers are detached when the effect tears down.docker-compose.yml-9-10 (1)
9-10:⚠️ Potential issue | 🟡 MinorReview the volume mount scope.
Mounting the entire current directory (
./) to/data/could inadvertently expose sensitive files (e.g.,.env,.git, credentials) to the container. Consider mounting only the specific directories needed.🛡️ Proposed fix to limit volume scope
volumes: - - ./:/data/ + - ./data:/data/Create a dedicated
datadirectory for FalkorDB persistence.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docker-compose.yml` around lines 9 - 10, The docker-compose volume mounts the entire repo (./) into the container as /data/, risking exposure of sensitive files; change the volume to mount only a dedicated data directory (e.g., create a local "data" folder) or specific required subfolders instead of "./", and update the volumes entry (the current "- ./:/data/") to reference that dedicated directory (or explicit paths) so only FalkorDB persistence data is exposed to the container.e2e/tests/nodeDetailsPanel.spec.ts-44-50 (1)
44-50:⚠️ Potential issue | 🟡 MinorAssert
targetNodeexists before clicking by coordinates.In this flow,
targetNodeis used directly forscreenX/screenY; add a guard to avoid accidental runtime failures.Suggested fix
const graphData = await codeGraph.getGraphNodes(); const targetNode = findNodeByName(graphData, node.nodeName); + expect(targetNode).toBeDefined(); + if (!targetNode) throw new Error(`Node not found: ${node.nodeName}`); await codeGraph.nodeClick(targetNode.screenX, targetNode.screenY);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@e2e/tests/nodeDetailsPanel.spec.ts` around lines 44 - 50, The test uses targetNode (returned from findNodeByName) directly for coordinates which can be undefined; add an explicit guard/assert after obtaining graphData and targetNode (from getGraphNodes and findNodeByName) to fail fast if targetNode is missing (e.g., expect(targetNode).toBeDefined() or throw a clear Error) before calling codeGraph.nodeClick(targetNode.screenX, targetNode.screenY), ensuring the test doesn't crash with an unclear runtime exception.e2e/logic/utils.ts-16-35 (1)
16-35:⚠️ Potential issue | 🟡 Minor
waitForStableTextcan silently return unstable text on timeout.Line 34 returns the last sampled text even if stabilization never occurred. That weakens test determinism.
Suggested fix
export const waitForStableText = async (locator: Locator, timeout: number = 5000): Promise<string> => { @@ - return stableText; + throw new Error(`Text did not stabilize within ${timeout}ms. Last value: "${stableText}"`); };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@e2e/logic/utils.ts` around lines 16 - 35, The waitForStableText helper (function waitForStableText) currently returns the last sampled text on timeout which can hide flaky failures; change it so that if the loop finishes without observing a stable, non-empty value it throws an Error (include the last sampled text and the timeout value in the message for debugging) instead of returning stableText, and keep the existing pollingInterval/maxChecks logic and the locator usage (locator.textContent(), locator.page().waitForTimeout()) intact.e2e/tests/canvas.spec.ts-128-131 (1)
128-131:⚠️ Potential issue | 🟡 MinorGuard fallback arrays before reading
.length.If both shapes are missing,
nodes/linksbecomeundefinedand the length checks crash.Suggested fix
- const nodes = result.elements?.nodes || result.nodes; - const links = result.elements?.links || result.links; + const nodes = result.elements?.nodes ?? result.nodes ?? []; + const links = result.elements?.links ?? result.links ?? []; expect(nodes.length).toBeGreaterThan(1); expect(links.length).toBeGreaterThan(1);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@e2e/tests/canvas.spec.ts` around lines 128 - 131, The test currently assumes nodes and links are arrays and reads .length directly, which will throw if both result.elements?.nodes and result.nodes are undefined; update the extraction to ensure a safe array fallback (e.g., use nullish coalescing or an explicit array check) so nodes and links are always arrays before asserting lengths—refer to the variables nodes and links and the expressions result.elements?.nodes || result.nodes (or replace with result.elements?.nodes ?? result.nodes ?? []) and similarly for links, then assert against the guaranteed array length.app/components/toolbar.tsx-10-10 (1)
10-10:⚠️ Potential issue | 🟡 MinorUnusual type signature for
setCooldownTicks.The type
(ticks?: 0) => voidonly allowsundefinedor the literal0. Based on usage inapp/page.tsxandapp/components/graphView.tsx, the function is called with-1as well (e.g.,setCooldownTicks(-1)). This type should be widened.Proposed fix
- setCooldownTicks: (ticks?: 0) => void + setCooldownTicks: (ticks: number | undefined) => void🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/components/toolbar.tsx` at line 10, The type for setCooldownTicks is too narrow — it currently permits only undefined or the literal 0; update its signature (in app/components/toolbar.tsx where setCooldownTicks is declared) to accept a number (e.g., ticks?: number or ticks: number | undefined) so callers like setCooldownTicks(-1) are allowed; ensure any related usages and prop types (where ToolbarProps or similar is defined) are updated accordingly to reflect the widened numeric type.app/page.tsx-338-346 (1)
338-346:⚠️ Potential issue | 🟡 MinorViewport check may exclude valid canvases.
The visibility check requires the canvas to be fully within the viewport (
rect.bottom <= window.innerHeight && rect.right <= window.innerWidth). If the canvas is larger than the viewport or partially scrolled, no canvas will match, causing a silent early return. Consider checking for intersection rather than full containment.Proposed fix for partial visibility
// Check if element is actually in viewport const rect = container.getBoundingClientRect(); - const isInViewport = rect.width > 0 && - rect.height > 0 && - rect.top >= 0 && - rect.left >= 0 && - rect.bottom <= window.innerHeight && - rect.right <= window.innerWidth; + const isInViewport = rect.width > 0 && + rect.height > 0 && + rect.top < window.innerHeight && + rect.left < window.innerWidth && + rect.bottom > 0 && + rect.right > 0; return isInViewport;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/page.tsx` around lines 338 - 346, The current isInViewport check in app/page.tsx requires full containment and will miss canvases that are larger than or partially in view; update the logic around the isInViewport calculation (the rect checks using rect.top/left/bottom/right and window.innerHeight/window.innerWidth) to test for intersection instead of full containment: keep rect.width > 0 && rect.height > 0, then require rect.bottom > 0 && rect.right > 0 && rect.top < window.innerHeight && rect.left < window.innerWidth so a canvas that is partially visible still counts as in-viewport.app/components/graphView.tsx-138-138 (1)
138-138:⚠️ Potential issue | 🟡 MinorDouble-click threshold of 1000ms is unusually long.
The typical double-click threshold is 300-500ms. A 1000ms threshold may cause unintended double-click detection from separate intentional clicks.
Proposed fix
- const isDoubleClick = now.getTime() - date.getTime() < 1000 && name === node.data.name + const isDoubleClick = now.getTime() - date.getTime() < 400 && name === node.data.name🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/components/graphView.tsx` at line 138, The double-click detection uses a 1000ms window which is too long; update the comparison in the isDoubleClick computation to use a standard threshold (e.g., 300–500ms) and preferably extract it to a named constant for clarity. Replace the hardcoded 1000 in the isDoubleClick expression (where now, date, name and node.data.name are used) with a constant like DOUBLE_CLICK_THRESHOLD = 500 and compare now.getTime() - date.getTime() < DOUBLE_CLICK_THRESHOLD so the threshold is easy to adjust and consistent.app/components/toolbar.tsx-62-66 (1)
62-66:⚠️ Potential issue | 🟡 MinorGuard against undefined
handleDownloadImagebefore invoking.
handleDownloadImageis optional (handleDownloadImage?: () => void), but theonClickhandler invokes it without a check. Clicking this button when the prop is undefined will cause a runtime error.Proposed fix
<button className="hidden md:block control-button" title="downloadImage" - onClick={handleDownloadImage} + onClick={() => handleDownloadImage?.()} >🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/components/toolbar.tsx` around lines 62 - 66, The Download button calls the optional handler directly and can throw if undefined; update the onClick usage in the Toolbar component to guard against undefined handleDownloadImage (e.g., use a safe call or only attach the handler when present), and consider disabling the button when handleDownloadImage is not provided so the UI reflects absence of an action; modify the JSX around the button (the element using className "hidden md:block control-button" and title "downloadImage") to use handleDownloadImage?.() or conditionally set onClick/disabled accordingly.app/components/elementMenu.tsx-125-136 (1)
125-136:⚠️ Potential issue | 🟡 MinorRedundant
onClickhandler on anchor element.The
<a>tag already hashrefandtarget="_blank", so theonClickhandler that callswindow.openwith the same URL is redundant and will cause the link to open twice in some browsers.Proposed fix
<a className="p-2" href={objURL} target="_blank" + rel="noopener noreferrer" title="Go to repo" - onClick={() => { - window.open(objURL, '_blank'); - }} > <Globe color="white" /> </a>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/components/elementMenu.tsx` around lines 125 - 136, Remove the redundant onClick handler on the anchor in elementMenu.tsx that calls window.open(objURL, '_blank'); the <a> already uses href={objURL} and target="_blank", so delete the onClick={() => { window.open(objURL, '_blank'); }} from the anchor (the element rendering the Globe icon) to prevent the link from opening twice in some browsers.app/components/code-graph.tsx-207-207 (1)
207-207:⚠️ Potential issue | 🟡 MinorUse block syntax for forEach callbacks to avoid implicit return values.
Lines 207 and 411 have arrow function callbacks with implicit returns (from
Set.add(...)and assignment), making the intent unclear and inconsistent with other forEach calls in the file.Proposed fixes
- deleteNeighbors(expandedNodes)?.forEach(id => deleteIdsMap.add(id)) + deleteNeighbors(expandedNodes)?.forEach(id => { + deleteIdsMap.add(id) + })- graph.Categories.forEach(c => c.show = true); + graph.Categories.forEach(c => { + c.show = true + });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/components/code-graph.tsx` at line 207, The forEach callbacks use concise arrow expressions that rely on implicit returns (e.g., deleteNeighbors(expandedNodes)?.forEach(id => deleteIdsMap.add(id))), which is inconsistent and unclear; update these to use block-bodied arrow functions with explicit statements (e.g., id => { deleteIdsMap.add(id); }) so intent is clear and matches other forEach usages — locate the occurrences around deleteNeighbors and the similar callback at line ~411 and replace the concise arrow bodies with block bodies containing explicit calls or assignments.
🧹 Nitpick comments (9)
e2e/config/constants.ts (1)
3-3: Fix the exported constant typo before it spreads (CHAT_OPTTIONS_COUNT).
Line 3 exports a misspelled public identifier. Consider adding a correctly spelled alias and keeping the old name temporarily for compatibility.Proposed non-breaking cleanup
-export const CHAT_OPTTIONS_COUNT = 6; +export const CHAT_OPTIONS_COUNT = 6; +export const CHAT_OPTTIONS_COUNT = CHAT_OPTIONS_COUNT; // backward compatibility alias🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@e2e/config/constants.ts` at line 3, Export a correctly spelled constant name and keep the misspelled one as a backward-compatible alias: add a new exported symbol CHAT_OPTIONS_COUNT with the same value as the existing CHAT_OPTTIONS_COUNT and re-export the old CHAT_OPTTIONS_COUNT (or assign it from the new constant) so consumers using the misspelled identifier keep working while code is migrated to CHAT_OPTIONS_COUNT.lib/utils.ts (1)
7-10: Avoidany[]in shared graph/message contracts.
Lines 8-9 and Line 34 remove most type safety from the new centralized model. Use concrete types and reusePathDatainMessageto prevent drift.Proposed typing cleanup
export type PathData = { - nodes: any[] - links: any[] + nodes: Node[] + links: Link[] } @@ export interface Message { type: MessageTypes; text?: string; - paths?: { nodes: any[], links: any[] }[]; + paths?: PathData[]; graphName?: string; }Also applies to: 31-35
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/utils.ts` around lines 7 - 10, The PathData type uses any[] for nodes and links and the Message type (referenced in this file) duplicates loose typing; replace any[] with concrete, shared types (e.g., define Node and Link interfaces or reuse existing ones) and update PathData to nodes: Node[] and links: Link[]; then change Message to reference PathData instead of duplicating fields so both the graph contract and Message reuse the same strongly typed structure (update all occurrences of PathData, Message, Node, and Link in this module).components/ui/carousel.tsx (2)
88-99: Keyboard navigation doesn't account for vertical orientation.The
handleKeyDownusesArrowLeft/ArrowRightregardless of orientation. For vertical carousels,ArrowUp/ArrowDownwould be more intuitive.♻️ Proposed enhancement
const handleKeyDown = React.useCallback( (event: React.KeyboardEvent<HTMLDivElement>) => { - if (event.key === "ArrowLeft") { + const prevKey = orientation === "horizontal" ? "ArrowLeft" : "ArrowUp" + const nextKey = orientation === "horizontal" ? "ArrowRight" : "ArrowDown" + if (event.key === prevKey) { event.preventDefault() scrollPrev() - } else if (event.key === "ArrowRight") { + } else if (event.key === nextKey) { event.preventDefault() scrollNext() } }, - [scrollPrev, scrollNext] + [orientation, scrollPrev, scrollNext] )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/ui/carousel.tsx` around lines 88 - 99, handleKeyDown always maps ArrowLeft/ArrowRight to scrollPrev/scrollNext, which ignores vertical carousels; update handleKeyDown to branch on the carousel orientation (e.g., an orientation prop or isVertical flag) and use ArrowUp/ArrowDown for vertical mode while keeping ArrowLeft/ArrowRight for horizontal mode, still calling scrollPrev and scrollNext and calling event.preventDefault() for handled keys; locate the callback by name handleKeyDown and update its dependency array if you reference any new orientation variable.
208-214: Minor: Remove extra space in className.There's a double space between
absoluteandh-8.🔧 Proposed fix
- "absolute h-8 w-8 rounded-full", + "absolute h-8 w-8 rounded-full",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/ui/carousel.tsx` around lines 208 - 214, The className string passed to the cn() call in the Carousel component has an extra space between "absolute" and "h-8"; update the class string inside the className prop (the cn(...) expression in components/ui/carousel.tsx where the element builds "absolute h-8 w-8 rounded-full") to remove the double space so it becomes "absolute h-8 w-8 rounded-full".README.md (1)
3-3: Consider using h2 for consistent heading levels.The heading jumps from h1 (
#) directly to h3 (###). For proper document structure, consider using h2 (##) instead.📝 Proposed fix
-### Visualize your repository with our graph for code analysis +## Visualize your repository with our graph for code analysis🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@README.md` at line 3, The heading "Visualize your repository with our graph for code analysis" is an h3 while the document starts with h1; change this header from "### Visualize your repository with our graph for code analysis" to an h2 by replacing the three hashes with two ("##") so heading levels are consistent and follow the h1 -> h2 structure in README.md.e2e/logic/POM/codeGraph.ts (1)
403-403: Magic number for animation delay.The 2000ms timeout is a hardcoded magic number. Consider extracting it to a named constant or making it configurable for better maintainability.
Proposed fix
+const GRAPH_ANIMATION_DELAY = 2000; + // In selectGraph method: - await this.page.waitForTimeout(2000); // graph animation delay + await this.page.waitForTimeout(GRAPH_ANIMATION_DELAY);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@e2e/logic/POM/codeGraph.ts` at line 403, The hardcoded 2000ms in the call to this.page.waitForTimeout(2000) is a magic number; extract it into a named constant (e.g., ANIMATION_DELAY_MS) or make it configurable (pass as a parameter or read from a config) and replace the literal in the method in codeGraph.ts so the delay is maintainable and easy to adjust; update any related tests or usages to reference the new constant/config option.app/components/ForceGraph.tsx (2)
154-156: Remove unusedcanvasReffromhandleEngineStopdependencies.
canvasRefis listed in the dependency array but is not used in the callback body. This can cause unnecessary re-creations of the callback.Proposed fix
const handleEngineStop = useCallback(() => { onEngineStop() - }, [canvasRef, onEngineStop]) + }, [onEngineStop])🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/components/ForceGraph.tsx` around lines 154 - 156, The handleEngineStop useCallback includes an unused dependency canvasRef which causes needless re-creations; update the dependency array for handleEngineStop to only include onEngineStop (i.e., useCallback(() => { onEngineStop() }, [onEngineStop])) so the callback only re-creates when onEngineStop changes and remove canvasRef from the dependency list.
106-151: Consider memoizing node/link lookups for better performance.The event handlers use
data.nodes.find()anddata.links.find()which are O(n) operations. For large graphs, this could impact performance during rapid interactions like hovering. Consider using a Map for O(1) lookups.Proposed optimization
+// Add memoized maps +const nodesMap = useMemo(() => new Map(data.nodes.map(n => [n.id, n])), [data.nodes]) +const linksMap = useMemo(() => new Map(data.links.map(l => [l.id, l])), [data.links]) // Map node click handler const handleNodeClick = useCallback((node: GraphNode, event: MouseEvent) => { - const originalNode = data.nodes.find(n => n.id === node.id) + const originalNode = nodesMap.get(node.id) if (originalNode) onNodeClick(originalNode, event) -}, [onNodeClick, data.nodes]) +}, [onNodeClick, nodesMap])Apply the same pattern to all other handlers (
handleNodeHover,handleNodeRightClick,handleLinkClick,handleLinkHover,handleLinkRightClick).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/components/ForceGraph.tsx` around lines 106 - 151, Replace repeated O(n) searches in handlers by building memoized lookup maps for nodes and links (e.g., nodeById and linkById) using useMemo derived from data.nodes and data.links, then change handleNodeClick, handleNodeHover, handleNodeRightClick, handleLinkClick, handleLinkHover, and handleLinkRightClick to use nodeById.get(node.id) / linkById.get(link.id) for O(1) lookups; update each handler's dependency array to depend on the corresponding memoized map (not the raw arrays) and preserve the current null-check behavior in handleNodeHover/handleLinkHover.app/components/elementMenu.tsx (1)
145-161: Redundant type cast after type guard.After the
"category" in objcheck on line 146, TypeScript narrowsobjtoNode. The explicit castobj as Nodeon lines 150 and 156 is redundant and can be removed for cleaner code.Proposed refactor
{ "category" in obj && <> <button className="p-2" - onClick={() => handleExpand([obj as Node], true)} + onClick={() => handleExpand([obj], true)} > <Maximize2 color="white" /> </button> <button className="p-2" - onClick={() => handleExpand([obj as Node], false)} + onClick={() => handleExpand([obj], false)} > <Minimize2 color="white" /> </button> </> }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/components/elementMenu.tsx` around lines 145 - 161, The "category" in obj type guard already narrows obj to Node, so remove the redundant casts obj as Node in the two onClick handlers and pass obj directly to handleExpand; update both calls inside the fragment (the Maximize2 and Minimize2 buttons) to use handleExpand([obj], true/false) so the code is cleaner and leverages TypeScript's narrowing (symbols: obj, Node, handleExpand).
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
package-lock.jsonis excluded by!**/package-lock.jsonpublic/code-graph-logo.svgis excluded by!**/*.svg
📒 Files selected for processing (39)
.github/dependabot.yml.github/workflows/nextjs.yml.github/workflows/playwright.ymlDockerfileREADME.mdapp/api/repo/route.tsapp/components/ForceGraph.tsxapp/components/Input.tsxapp/components/chat.tsxapp/components/code-graph.tsxapp/components/combobox.tsxapp/components/dataPanel.tsxapp/components/elementMenu.tsxapp/components/graphView.tsxapp/components/model.tsapp/components/toolbar.tsxapp/globals.cssapp/layout.tsxapp/page.tsxcomponents.jsoncomponents/ui/carousel.tsxcomponents/ui/drawer.tsxcomponents/ui/switch.tsxdocker-compose.ymle2e/config/constants.tse2e/config/testData.tse2e/infra/ui/basePage.tse2e/infra/ui/browserWrapper.tse2e/logic/POM/codeGraph.tse2e/logic/utils.tse2e/tests/canvas.spec.tse2e/tests/chat.spec.tse2e/tests/navBar.spec.tse2e/tests/nodeDetailsPanel.spec.tse2e/tests/searchBar.spec.tslib/utils.tspackage.jsonplaywright.config.tstailwind.config.js
💤 Files with no reviewable changes (1)
- app/layout.tsx
| npm run build | ||
| NEXTAUTH_SECRET=SECRET npm start & | ||
| npx playwright test --shard=${{ matrix.shard }}/2 --reporter=dot,list | ||
| NEXTAUTH_SECRET=SECRET npm start & npx playwright test --shard=${{ matrix.shard }}/2 --reporter=dot,list |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# 1. Check the exact content of the playwright workflow at the mentioned lines
cat -n .github/workflows/playwright.yml | sed -n '35,45p'Repository: FalkorDB/code-graph
Length of output: 552
🏁 Script executed:
#!/bin/bash
# 2. Find and examine playwright.config.ts
fd -t f 'playwright.config.*' -x cat -n {} | head -100Repository: FalkorDB/code-graph
Length of output: 2945
🏁 Script executed:
#!/bin/bash
# 3. Search for any existing readiness wait mechanisms in the workflow
rg -n 'curl|wait-on|webServer|healthcheck' .github/workflows/ --type ymlRepository: FalkorDB/code-graph
Length of output: 90
🏁 Script executed:
#!/bin/bash
# 4. Check for reporter configuration in playwright config
rg -n 'reporter' . --type ts --type js | head -20Repository: FalkorDB/code-graph
Length of output: 249
Fix CI startup race and prevent Playwright reporter override.
Line 39 starts the app without a readiness check (npm start &) and immediately runs tests, which can cause flaky failures. Additionally, the --reporter=dot,list flag overrides the playwright.config.ts reporter configuration ([["html", { outputFolder: "playwright-report" }]]), preventing HTML report generation while the artifact upload step still expects it.
Suggested workflow fix
- NEXTAUTH_SECRET=SECRET npm start & npx playwright test --shard=${{ matrix.shard }}/2 --reporter=dot,list
+ NEXTAUTH_SECRET=SECRET npm start &
+ for i in {1..60}; do
+ curl -fsS http://127.0.0.1:3000 >/dev/null && break
+ sleep 1
+ done
+ npx playwright test --shard=${{ matrix.shard }}/2📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| NEXTAUTH_SECRET=SECRET npm start & npx playwright test --shard=${{ matrix.shard }}/2 --reporter=dot,list | |
| NEXTAUTH_SECRET=SECRET npm start & | |
| for i in {1..60}; do | |
| curl -fsS http://127.0.0.1:3000 >/dev/null && break | |
| sleep 1 | |
| done | |
| npx playwright test --shard=${{ matrix.shard }}/2 |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/playwright.yml at line 39, The CI starts the app in
background with `npm start &` and immediately runs `npx playwright test` (with
`--reporter=dot,list`), causing a race and overriding the reporters defined in
`playwright.config.ts` (which expects the HTML reporter). Fix by replacing the
fire-and-forget start with a readiness wait strategy (e.g., use a
wait-on/start-server-and-test style flow or run `npm start` then wait for the
app URL/port to be reachable before running tests) and remove the
`--reporter=dot,list` flag from the `npx playwright test` invocation so the
reporters in `playwright.config.ts` (HTML output folder) are used and the
subsequent artifact upload will find the HTML report.
| if (!repo_url) { | ||
| throw new Error("URL parameter is missing"); | ||
| } | ||
|
|
||
| const { url, token } = getEnvVariables(); | ||
|
|
||
| const isLocal = repo_url.startsWith("file://") | ||
|
|
||
| const result = await fetch(`${url}/${isLocal ? "analyze_folder" : "analyze_repo"}`, { | ||
| method: 'POST', | ||
| body: JSON.stringify({ repo_url, ignore: ["./.github", "./sbin", "./.git", "./deps", "./bin", "./build"] }), |
There was a problem hiding this comment.
Block untrusted file:// inputs before selecting analyze_folder.
At Line 41, endpoint selection is fully client-controlled. A caller can submit url=file:///... and trigger local-folder analysis on the server/backend. This should be explicitly gated (or rejected) in this public API route.
🔒 Suggested hardening
export async function POST(request: NextRequest) {
const repo_url = request.nextUrl.searchParams.get('url');
try {
if (!repo_url) {
throw new Error("URL parameter is missing");
}
+
+ const parsed = new URL(repo_url);
+ const allowLocalAnalysis = process.env.ENABLE_LOCAL_ANALYSIS === "true";
+ if (parsed.protocol === "file:" && !allowLocalAnalysis) {
+ throw new Error("Local file analysis is disabled");
+ }
+ if (!["file:", "http:", "https:"].includes(parsed.protocol)) {
+ throw new Error("Unsupported URL scheme");
+ }
const { url, token } = getEnvVariables();
- const isLocal = repo_url.startsWith("file://")
+ const isLocal = parsed.protocol === "file:"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (!repo_url) { | |
| throw new Error("URL parameter is missing"); | |
| } | |
| const { url, token } = getEnvVariables(); | |
| const isLocal = repo_url.startsWith("file://") | |
| const result = await fetch(`${url}/${isLocal ? "analyze_folder" : "analyze_repo"}`, { | |
| method: 'POST', | |
| body: JSON.stringify({ repo_url, ignore: ["./.github", "./sbin", "./.git", "./deps", "./bin", "./build"] }), | |
| if (!repo_url) { | |
| throw new Error("URL parameter is missing"); | |
| } | |
| const parsed = new URL(repo_url); | |
| const allowLocalAnalysis = process.env.ENABLE_LOCAL_ANALYSIS === "true"; | |
| if (parsed.protocol === "file:" && !allowLocalAnalysis) { | |
| throw new Error("Local file analysis is disabled"); | |
| } | |
| if (!["file:", "http:", "https:"].includes(parsed.protocol)) { | |
| throw new Error("Unsupported URL scheme"); | |
| } | |
| const { url, token } = getEnvVariables(); | |
| const isLocal = parsed.protocol === "file:" | |
| const result = await fetch(`${url}/${isLocal ? "analyze_folder" : "analyze_repo"}`, { | |
| method: 'POST', | |
| body: JSON.stringify({ repo_url, ignore: ["./.github", "./sbin", "./.git", "./deps", "./bin", "./build"] }), |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/api/repo/route.ts` around lines 35 - 45, The route currently lets clients
pick analyze_folder vs analyze_repo by sending a file:// URL; update the
validation around repo_url (the variable used when building the fetch call) so
file:// inputs are blocked or require explicit server-side authorization before
selecting analyze_folder. In practice, inside the handler that reads repo_url
and before calling fetch(`${url}/${isLocal ? "analyze_folder" :
"analyze_repo"}`...), enforce that if repo_url.startsWith("file://") you either
return a 400 error or only allow it when a secure server-only condition is met
(e.g., an authenticated admin check or an allowlist flag from
getEnvVariables()); ensure all branches use that validation so analyze_folder
cannot be triggered by arbitrary clients.
| const result = await fetch(`${url}/${isLocal ? "analyze_folder" : "analyze_repo"}`, { | ||
| method: 'POST', | ||
| body: JSON.stringify({ repo_url, ignore: ["./.github", "./sbin", "./.git", "./deps", "./bin", "./build"] }), | ||
| headers: { | ||
| "Authorization": token, | ||
| 'Content-Type': 'application/json' | ||
| }, | ||
| cache: 'no-store' | ||
| }); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Read the specific file to verify the code at lines 43-51
cat -n app/api/repo/route.ts | sed -n '40,55p'Repository: FalkorDB/code-graph
Length of output: 624
🏁 Script executed:
# Search for signal/timeout patterns in API routes
rg -nP --type=ts 'signal\s*:|AbortSignal|timeout' app/api/Repository: FalkorDB/code-graph
Length of output: 45
🏁 Script executed:
# Look at all fetch calls in the api routes to understand the pattern
rg -nP --type=ts 'fetch\(' app/api -A 5 -B 1Repository: FalkorDB/code-graph
Length of output: 4398
Add a timeout to backend fetch to prevent hung requests.
The POST call at lines 43–51 lacks an abort signal, so a stalled backend can indefinitely tie up this route. All fetch calls across the API routes have this vulnerability.
Suggested fix
const result = await fetch(`${url}/${isLocal ? "analyze_folder" : "analyze_repo"}`, {
method: 'POST',
body: JSON.stringify({ repo_url, ignore: ["./.github", "./sbin", "./.git", "./deps", "./bin", "./build"] }),
headers: {
"Authorization": token,
'Content-Type': 'application/json'
},
+ signal: AbortSignal.timeout(30_000),
cache: 'no-store'
});📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const result = await fetch(`${url}/${isLocal ? "analyze_folder" : "analyze_repo"}`, { | |
| method: 'POST', | |
| body: JSON.stringify({ repo_url, ignore: ["./.github", "./sbin", "./.git", "./deps", "./bin", "./build"] }), | |
| headers: { | |
| "Authorization": token, | |
| 'Content-Type': 'application/json' | |
| }, | |
| cache: 'no-store' | |
| }); | |
| const result = await fetch(`${url}/${isLocal ? "analyze_folder" : "analyze_repo"}`, { | |
| method: 'POST', | |
| body: JSON.stringify({ repo_url, ignore: ["./.github", "./sbin", "./.git", "./deps", "./bin", "./build"] }), | |
| headers: { | |
| "Authorization": token, | |
| 'Content-Type': 'application/json' | |
| }, | |
| signal: AbortSignal.timeout(30_000), | |
| cache: 'no-store' | |
| }); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/api/repo/route.ts` around lines 43 - 51, The fetch call that assigns
result (the POST to `${url}/${isLocal ? "analyze_folder" : "analyze_repo"}`)
needs an AbortController-based timeout so requests can't hang indefinitely:
create an AbortController, start a timer (e.g., 15–30s) that calls
controller.abort(), pass controller.signal in the fetch options (alongside
method, body, headers, cache), and clear the timer after fetch completes; also
catch the abort/DOMException and handle it (return a 504 or throw a clear
error). Update the code around the existing result = await fetch(...) invocation
and ensure the timer is cleared in both success and error paths.
| if (!result.ok) { | ||
| throw new Error(await result.text()); | ||
| } | ||
|
|
||
| return NextResponse.json({ message: "success" }, { status: 200 }); | ||
| } catch (err) { | ||
| console.error(err) | ||
| return NextResponse.json(err instanceof Error ? err.message : String(err), { status: 400 }); |
There was a problem hiding this comment.
Do not expose raw upstream error bodies to clients.
Lines 54-60 currently propagate backend error text directly in the API response. Return a sanitized message and keep detailed text in server logs only.
🛡️ Suggested error sanitization
if (!result.ok) {
- throw new Error(await result.text());
+ const upstream = await result.text();
+ console.error("Repository analysis failed", { status: result.status, upstream });
+ throw new Error("Repository analysis failed");
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (!result.ok) { | |
| throw new Error(await result.text()); | |
| } | |
| return NextResponse.json({ message: "success" }, { status: 200 }); | |
| } catch (err) { | |
| console.error(err) | |
| return NextResponse.json(err instanceof Error ? err.message : String(err), { status: 400 }); | |
| if (!result.ok) { | |
| const upstream = await result.text(); | |
| console.error("Repository analysis failed", { status: result.status, upstream }); | |
| throw new Error("Repository analysis failed"); | |
| } | |
| return NextResponse.json({ message: "success" }, { status: 200 }); | |
| } catch (err) { | |
| console.error(err) | |
| return NextResponse.json(err instanceof Error ? err.message : String(err), { status: 400 }); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/api/repo/route.ts` around lines 53 - 60, The handler currently throws and
returns raw upstream error text (see the throw new Error(await result.text())
and the catch that returns err.message via NextResponse.json), which exposes
sensitive details; instead, in the try/catch keep logging the full upstream
text/stack to server logs (console.error or process logger) but return a
sanitized JSON response via NextResponse.json with a generic message (e.g.,
"Upstream service error" or "Internal server error") and an appropriate status
code; update the throw site (inside the result.ok check) to throw a generic
Error like new Error("Upstream service returned an error") or capture the
upstream body into a local variable for logging, and modify the catch block to
log the full error (and body) and respond with a sanitized message rather than
err.message.
| setPaths: Dispatch<SetStateAction<PathData[]>> | ||
| } | ||
|
|
||
| const PATH_COLOR = "#ffde21"; |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Duplicate PATH_COLOR constant.
PATH_COLOR is defined here and also exported from lib/utils.ts (line 43). Import it from lib/utils.ts instead to maintain a single source of truth.
Proposed fix
-import { cn, GraphRef } from "@/lib/utils";
+import { cn, GraphRef, PATH_COLOR } from "@/lib/utils";
...
-const PATH_COLOR = "#ffde21";📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const PATH_COLOR = "#ffde21"; | |
| import { cn, GraphRef, PATH_COLOR } from "@/lib/utils"; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/components/chat.tsx` at line 35, Remove the duplicate local constant
PATH_COLOR in app/components/chat.tsx and instead import the shared PATH_COLOR
exported from lib/utils.ts; locate and delete the line declaring const
PATH_COLOR = "#ffde21" and add an import for PATH_COLOR from 'lib/utils' so all
uses in the Chat component reference the single exported symbol.
| : typeof value === "object" ? | ||
| <JSONTree | ||
| data={Object.fromEntries(Object.entries(value).filter(([k]) => !excludedProperties.includes(k)))} | ||
| theme={{ |
There was a problem hiding this comment.
Object.entries(value) can throw when value is null.
typeof null === "object", so this branch needs a null guard.
Suggested fix
- : typeof value === "object" ?
+ : value !== null && typeof value === "object" ?
<JSONTree📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| : typeof value === "object" ? | |
| <JSONTree | |
| data={Object.fromEntries(Object.entries(value).filter(([k]) => !excludedProperties.includes(k)))} | |
| theme={{ | |
| : value !== null && typeof value === "object" ? | |
| <JSONTree | |
| data={Object.fromEntries(Object.entries(value).filter(([k]) => !excludedProperties.includes(k)))} | |
| theme={{ |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/components/dataPanel.tsx` around lines 74 - 77, The branch rendering
<JSONTree> assumes value is an object but misses a null guard (since typeof null
=== "object"); update the condition that chooses the <JSONTree> branch to ensure
value is non-null (e.g., check value !== null && typeof value === "object") or
replace Object.entries(value) with Object.entries(value ?? {}) so Object.entries
never receives null; adjust the use around JSONTree, Object.entries, and
excludedProperties to operate only when value is a real object to avoid a
runtime exception.
| <a | ||
| className="flex items-center gap-2 p-2" | ||
| href={url} | ||
| target="_blank" | ||
| title="Go to repo" | ||
| onClick={() => { | ||
| const newTab = window.open(url, '_blank'); | ||
|
|
||
| if (!obj.src_start || !obj.src_end || !newTab) return | ||
| if (!obj.data.src_start || !obj.data.src_end || !newTab) return | ||
|
|
||
| newTab.scroll({ | ||
| top: obj.src_start, | ||
| behavior: 'smooth' | ||
| }) | ||
| }} | ||
| > | ||
| <SquareArrowOutUpRight color="white" /> | ||
| Go to repo | ||
| </a> | ||
| </footer> | ||
| </div> | ||
| newTab.scroll({ | ||
| top: obj.data.src_start, | ||
| behavior: 'smooth' | ||
| }) | ||
| }} |
There was a problem hiding this comment.
Avoid combining href navigation with window.open in onClick.
This can open two tabs (browser default + explicit window.open) and makes navigation behavior inconsistent.
Suggested fix
<a
className="flex items-center gap-2 p-2"
- href={url}
+ href={obj.data.src_start ? `${url}#L${obj.data.src_start}` : url}
target="_blank"
+ rel="noopener noreferrer"
title="Go to repo"
- onClick={() => {
- const newTab = window.open(url, '_blank');
-
- if (!obj.data.src_start || !obj.data.src_end || !newTab) return
-
- newTab.scroll({
- top: obj.data.src_start,
- behavior: 'smooth'
- })
- }}
>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <a | |
| className="flex items-center gap-2 p-2" | |
| href={url} | |
| target="_blank" | |
| title="Go to repo" | |
| onClick={() => { | |
| const newTab = window.open(url, '_blank'); | |
| if (!obj.src_start || !obj.src_end || !newTab) return | |
| if (!obj.data.src_start || !obj.data.src_end || !newTab) return | |
| newTab.scroll({ | |
| top: obj.src_start, | |
| behavior: 'smooth' | |
| }) | |
| }} | |
| > | |
| <SquareArrowOutUpRight color="white" /> | |
| Go to repo | |
| </a> | |
| </footer> | |
| </div> | |
| newTab.scroll({ | |
| top: obj.data.src_start, | |
| behavior: 'smooth' | |
| }) | |
| }} | |
| <a | |
| className="flex items-center gap-2 p-2" | |
| href={obj.data.src_start ? `${url}#L${obj.data.src_start}` : url} | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| title="Go to repo" | |
| > |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/components/dataPanel.tsx` around lines 133 - 147, The anchor combines an
href with an onClick that also calls window.open, causing duplicate tabs; in the
anchor handler (the <a> element using url, target="_blank" and the onClick that
creates newTab) either remove the href/target and use window.open exclusively,
or keep href/target and call event.preventDefault() at the top of the onClick
and then perform a single window.open(url, '_blank') before attempting to scroll
using obj.data.src_start/obj.data.src_end (check newTab truthiness). Update the
code in dataPanel.tsx to use one navigation mechanism and ensure scrolling runs
only on the single opened tab (use newTab.scroll when newTab exists).
| - OPENAI_API_KEY=YOUR_OPENAI_API_KEY | ||
| - SECRET_TOKEN=sC0tTerMania |
There was a problem hiding this comment.
Avoid hardcoding secrets in docker-compose.yml.
The SECRET_TOKEN has a hardcoded value that will be committed to version control. This is a security risk as secrets should not be stored in plain text in the repository.
Consider using environment variable substitution or a .env file:
🔒 Proposed fix using environment variable substitution
environment:
- FALKORDB_HOST=falkordb
- FALKORDB_PORT=6379
- - OPENAI_API_KEY=YOUR_OPENAI_API_KEY
- - SECRET_TOKEN=sC0tTerMania
+ - OPENAI_API_KEY=${OPENAI_API_KEY}
+ - SECRET_TOKEN=${SECRET_TOKEN}
- FLASK_RUN_HOST=0.0.0.0
- FLASK_RUN_PORT=5000
- CODE_GRAPH_PUBLIC=1Then create a .env.example file with placeholder values and add .env to .gitignore.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - OPENAI_API_KEY=YOUR_OPENAI_API_KEY | |
| - SECRET_TOKEN=sC0tTerMania | |
| - OPENAI_API_KEY=${OPENAI_API_KEY} | |
| - SECRET_TOKEN=${SECRET_TOKEN} |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docker-compose.yml` around lines 33 - 34, The docker-compose.yml currently
hardcodes SECRET_TOKEN which exposes credentials; change the service environment
to read SECRET_TOKEN from an external source (use environment variable
substitution like ${SECRET_TOKEN} or reference a .env file) and update
documentation by adding a .env.example with a placeholder SECRET_TOKEN and
adding .env to .gitignore; ensure any code/config reading SECRET_TOKEN (e.g.,
the service that expects SECRET_TOKEN) continues to work with the new env
lookup.
| for (let index = 1; index < 6; index++) { | ||
| const questionNumber = index + 1; | ||
| test(`Validate displaying question ${index} in chat after selection from options menu`, async () => { | ||
| const chat = await browser.createNewPage(CodeGraph, urls.baseUrl); | ||
| await chat.selectGraph(GRAPHRAG_SDK); | ||
| await chat.clickOnQuestionOptionsMenu(); | ||
| const selectedQuestion = await chat.selectAndGetQuestionInOptionsMenu(questionNumber.toString()); | ||
| await chat.clickOnLightBulbBtn(); | ||
| const selectedQuestion = await chat.selectAndGetQuestionInOptionsMenu(questionNumber.toString()); | ||
| expect(selectedQuestion).toEqual(await chat.getLastQuestionInChat()) |
There was a problem hiding this comment.
Question-option loop is off by one.
Line 123 starts at 1, and Line 124 adds +1, so the selected option range becomes 2..6 (skips first option and can exceed available options).
Suggested fix
- for (let index = 1; index < 6; index++) {
- const questionNumber = index + 1;
+ for (let index = 1; index <= CHAT_OPTTIONS_COUNT; index++) {
+ const questionNumber = index;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@e2e/tests/chat.spec.ts` around lines 123 - 130, The loop computes
questionNumber incorrectly by adding +1, causing it to select options 2..6 and
skip the first option; change the assignment so questionNumber equals the loop
index (i.e., let questionNumber = index) or remove the +1 usage so calls to
chat.selectAndGetQuestionInOptionsMenu(questionNumber.toString()) and the
corresponding expect compare the intended option (ensure the loop bounds still
match available options). This touches the for loop and the variable
questionNumber in the test and the call to
chat.selectAndGetQuestionInOptionsMenu/getLastQuestionInChat.
| const graphData = await codeGraph.getGraphNodes(); | ||
|
|
||
| // Find a node that has src property (actual source code) | ||
| const targetNode = graphData.find(node => node.src) || graphData[0]; | ||
| const nodeName = targetNode.name || targetNode.data?.name; | ||
| await codeGraph.fillSearchBar(nodeName); | ||
| await codeGraph.selectSearchBarOptionBtn("1"); |
There was a problem hiding this comment.
Guard empty graph state before deriving nodeName.
If graphData is empty, Line 78 dereferences undefined and the test fails before validating panel behavior.
Suggested fix
const graphData = await codeGraph.getGraphNodes();
+ expect(graphData.length).toBeGreaterThan(0);
const targetNode = graphData.find(node => node.src) || graphData[0];
const nodeName = targetNode.name || targetNode.data?.name;
+ expect(nodeName).toBeDefined();
+ if (!nodeName) throw new Error("Unable to resolve node name for search");
await codeGraph.fillSearchBar(nodeName);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const graphData = await codeGraph.getGraphNodes(); | |
| // Find a node that has src property (actual source code) | |
| const targetNode = graphData.find(node => node.src) || graphData[0]; | |
| const nodeName = targetNode.name || targetNode.data?.name; | |
| await codeGraph.fillSearchBar(nodeName); | |
| await codeGraph.selectSearchBarOptionBtn("1"); | |
| const graphData = await codeGraph.getGraphNodes(); | |
| expect(graphData.length).toBeGreaterThan(0); | |
| const targetNode = graphData.find(node => node.src) || graphData[0]; | |
| const nodeName = targetNode.name || targetNode.data?.name; | |
| expect(nodeName).toBeDefined(); | |
| if (!nodeName) throw new Error("Unable to resolve node name for search"); | |
| await codeGraph.fillSearchBar(nodeName); | |
| await codeGraph.selectSearchBarOptionBtn("1"); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@e2e/tests/nodeDetailsPanel.spec.ts` around lines 76 - 80, The test assumes
graphData from codeGraph.getGraphNodes() is non-empty; guard against an empty
array before deriving targetNode/nodeName by checking graphData.length (or that
targetNode is defined) and handling the empty case (e.g., fail early with a
clear message or skip the subsequent steps). Update the block around
codeGraph.getGraphNodes(), targetNode, and nodeName to validate graphData and
targetNode and only call codeGraph.fillSearchBar(nodeName) and
codeGraph.selectSearchBarOptionBtn("1") when a valid nodeName exists; reference
the existing functions codeGraph.getGraphNodes, the local targetNode/nodeName
variables, and the methods fillSearchBar and selectSearchBarOptionBtn to locate
and apply the guard.
Summary by CodeRabbit